
#include <stdio.h>

/*1.算术操作符
 *	+
 *	-
 *	*
 *	/	编程中，除法只会保留整数
 *	%	取余，例如：个位数中 1—9分别对10取余都等于它本身，那么1-99对100取余就是他本身
 */
/*********************************************************************************************************************************************************************/

/*2.移位操作符
 *	<<	左移操作符：左边舍去，右边补0
 *	>>	右移操作符：1.逻辑右移：右边舍去，左边补0	2.算术右移： 左边用原该值的符号位填充，右边丢弃
 */
					//在计算机中，是整型以二进制补码形式存储的，正数的补码反码都一样，而负数的补码是其，正数的补码按位取反，末尾+1求得的，且最高位是符号位
					//例如5的补码是 0000 0101 按位取反 1111 1010 末尾+1 1111 1011
					//逻辑右移就是：不管其正负，右边舍去 左边补0 如 1111 1011 >> 1 = 0111 1101 
					//算术右移是：	符号位是什么，右边就补什么	 如 1111 1011 >> 1 = 1111 1101 = -1按位取反 = -3
					//但是 不可以移负位，要么左移，要么右移
/*********************************************************************************************************************************************************************/


/*3.位操作符（必须是整数）
 *	&	//按位与		————	全1出1，有0出0
 *	|	//按位或		————	有1出1，全0出0
 *	^	//按位异或		————	相同出0，相异为1
 */
//例如：不能创建临时变量（第三个变量），实现两个数交换
/*
int main()
{
	int a = 10;
	int b = 20;
	a=a^b;					//也可以 a=a+b
	b = a^b;				//		 b=a-b
	a = a^b;				//		 a=a-b
	printf("a = %d b = %d\n", a, b);
	return 0;
}
*/


//例2 求一个整数存储在内存中的二进制中1的个数。
/*
int main()
{
	int a;
	int i = 0;
	int count = 0;
	scanf("%d",&a);
	for(i = 0; i < 32; i++)
	{
		if( ((a>>i)&1) == 1 )
			count++;
	}
	printf("%d\n",count);
}



//方法2
#include <stdio.h>
int main()
{
	 int num = -1;
	 int i = 0;
	 int count = 0;//计数
	 while(num)
	 {
		count++;				//每次都能与走一个一，一共与几次就是与走几个1，就是每次把原式处在最右侧的1与走
		num = num&(num-1);		
	 }
	 printf("二进制中1的个数 = %d\n",count);
	 return 0; 
}

//利用方法二的 num = &num(num-1)，求一个整数n是不是2的幂次方 即n=2^ma
//2——> 10 ;4——> 100; 8———>1000; 16——>10000；可知幂次方的二进制都只有一个1，
//if(n&(n-1) == 0)即可求得

//方法3
//unsigned int n
whiel(n)
{
	if(n%2==1)
		count++
	n=n/2
}
//但是有死循环的风险
*/



//例3 求两个数在二进制中不同位的个数
//方法1
/*
int main()
{
	int i,a,b;
	int count = 0;
	scanf("%d%d",&a,&b);
	for(i = 0;i < 32;i++)
	{
		if( ((a>>i)&1) != ((b>>i)&1))
			count++;
	}
	printf("%d\n",count);

	return 0;
}
*/

//方法二
/*
int main()
{
	int a,b,temp;
	int i,count = 0;
	scanf("%d%d",&a,&b);
	temp = a^b;					//先异或 在求个数,可以把上面那个&本身-1的封装成函数，在传递异或
	for(i = 0;i < 32;i++)
	{
		if((temp>>i)&1 == 1)
			count++;
	}
	printf("%d\n",count);
	return 0;
}
*/

//例4 打印一个数的二进制 奇数位和偶数位
/*
int main()
{
	int a;
	int i;
	scanf("%d",&a);
	printf("偶数位：");
	for(i = 31;i>=0;i-=2)		//先看首位怎么移到，再看末尾怎么移到，二进制奇偶是从右往左数
		printf("%d",(a>>i) & 1);
	printf("\n");
	printf("奇数位：");
	for(i = 30;i>=0;i-=2)
		printf("%d",(a>>i) & 1);

	return 0;
}
*/

/*********************************************************************************************************************************************************************/
/*4.赋值运算符
 *	=	
 *	+=					a+=1   —————— a=a+1
 *	-=
 *	*=
 *	/=
 *	%=
 *	>>=
 *	<<=
 *	&=
 *	|=
 *	^=
 */

/*********************************************************************************************************************************************************************/

/*5.单目运算符
 *	！							逻辑反
 *	+							正值
 *	-							负值
 *	&							取地址符
 *	sizeof						操作数的类型长度（字节为单位）
 *	i++							后置++ 先输出i，后+1
 *	++i							前置++ 先+1，后输出
 *	i--							
 *	--i
 *	*							间接访问操作符（解引用操作符）
 *	~							按位取反     如 1111 按位取反就是 0000
 *	(类型名)					强制类型转化
 */ 
/*
int main()
{
	int i = 20;
	char arr[20] = {0};
	int *p;

	printf("%d,",!2);					//整数取反是0
	printf("%d\n",!0);					//0取反是1

	putchar('\n');

	printf("%d,",sizeof(i));			//i是int型大小是4字节
	printf("%d\n",sizeof(int));			//4
	
	putchar('\n');
	
	printf("%d,",sizeof(arr));			//数组名是整个数组的地址，所以是20
	printf("%d\n",sizeof(arr[0]));		//一个元素的地址，则为1
	
	putchar('\n');
	
	i = -i;								//i = -20；
	printf("%d,",i);					//-20
	printf("%d\n",-i);					// -（i） - -20 =20
	
	putchar('\n');
	
	p = &i;								//指针接收i的地址
	printf("i = %d,",i);				//i的内容
	printf("&i = %d,",&i);				//i的地址以十进制输出
	printf("&i = %p\n",&i);				//i的地址以十六进制输出
	
	putchar('\n');
	
	printf("*p = %d,",*p);				//指针解引用，就是i的内容
	printf("&(*p) = %d,",&*p);			//取地址与解引用抵消，即 对p的内容以十进制输出，p内容是i的地址		
	printf("p = %p\n",&p);				//指针自己的地址
	printf("可知 &*p = &i，&*互相抵消\n");

	putchar('\n');

	printf("%d\n",sizeof((char)i));		//强制类型转换

	return 0;
}

//while（）中的跳出条件 ~scanf(%d,&n) 等价 scanf()!=EOF									EOF=-1
//如果scanf没有成功读取，返回0，取反的结果不为假，继续执行如果没有输入，到达文件末尾则返回-1，取反的结果为0，结束while   
*/
/*********************************************************************************************************************************************************************/
/*6.关系操作符
 *	>		大于
 *	<		小于
 *	>=		大于等于
 *	<=		小于等于
 *	!=		不等于
 *	==		等于
 */

//if("abcd" == "abcd")//虽然语法没错，但比较的是字符串首地址
//若比较字符串大小可以用 strcmp
//strcmp 比较对应位置上，字符的大小，不是长短 ，比如 abcde 和abbdepq ，比较到第三位C比b大 所以第一个字串大 即只要发现不相等的字符就完成比较

/*********************************************************************************************************************************************************************/
/*7.逻辑操作符
 *	&&		与
 *	||		或
 */
//逻辑与按位的区别
//int main()
//{
//	int i = 1;
//	int z = 2;
//	int j = 0;
//	
//	
//	if( j && z && i )				//只要第一个表达式为假则直接跳出，因为与是全1出1
//	{
//		j++;
//		z++;
//		i++;
//	}
//	printf("j=%d,i=%d,z=%d\n",j,i,z);
//
//	if(i++ || z++ || j++)			//只要第一个表达式是真，后面就不执行了，因为有1出1
//		printf("j=%d,i=%d,z=%d\n",j,i,z);
//}

/*
int main()
{
	 int i = 0,a=0,b=2,c =3,d=4;
	// i = a++ && ++b && d++;
	 i = a++||++b||d++;
	 printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
	 return 0; 
}
//&&的答案是 a=1,b=2,c=3,d=4
//||的答案是 a=1,b=3,c=3,d=4		//因为第一个表达式 是先输出后++ a为假则判断第二个表达式，++b是先加后输出为真则直接退出
*/
/*********************************************************************************************************************************************************************/
/*8.条件操作符
 *	exp1 ? exp2 : exp3		
 */
/*
int main()
{
	int a = 2;
	int b = 3;
	int i;
	i = (a>b? a++:++b);
	printf("%d\n",i);				//a>b 为假 执行 ++b，就是先+后输出 所以为4，若是b++ 则为3，因为先输出后执行
	return 0;
}
*/
/*********************************************************************************************************************************************************************/
/*9.逗号表达式
 *	exp1, exp2, exp3, …expN		逗号表达式，就是用逗号隔开的多个表达式。 逗号表达式，从左向右依次执行。整个表达式的结果是最后一个表达式的结果。	
 */
/*
#include <stdio.h>
int main()
{
	int a, b, c;
	a = 5;
	c = ++a;
	b = ++c, c++, ++a, a++;							//第一个++c算完后就给了b 
	b += a++ + c;									//若是（++c, c++, ++a, a++）则取最后一个表达式的值
	printf("a = %d b = %d c = %d\n:", a, b, c);
	return 0;
}
//b = ++c, c++, ++a, a++;							b = (++c, c++, ++a, a++);
//首先 a=6,c=6,之后c=7,b=7,c=8,a=7,a=8				首先 a=6,c=6,之后c=7,c=8,a=7,b=7,a=8  因为a++是先输出后执行

int main()
{
	int a = 1;
	int b = 2;
	int c = 0;
	int d = 0; 
	d = a>b, a=b+10, a, b=a+1;
	c = (a>b, a=b+10, a, b=a+1); 
	printf("%d,%d",c,d);			//c为末尾值13，d为首值为假 0
}
*/
/*********************************************************************************************************************************************************************/
/*10.下标引用、函数调用和结构成员
 *	[]		下标引用操作符				通过[] 引用数组成员
 *	()		函数调用操作符				（）函数调用操作符 接受一个或者多个操作数：第一个操作数是函数名，剩余的操作数就是传递给函数的参数
 */

/*********************************************************************************************************************************************************************/
/*11.访问一个结构的成员
 *	结构体.成员名		
 *	结构体指针->成员名		
 */
/*********************************************************************************************************************************************************************/
//隐式类型转换
/*
在表达式中的字符型和短整型操作数在使用之前会被转换为普通整型(int)，这种转换称为整型提升
例如：
1.负数的整型提升：
char ch = -1;
int  i = 1;
i=i+ch;
-1的补码是 1111 1111 ，所以在操作之前会被转为整型 
负数隐式转换是：以高位符号位为准补1 11111111 11111111 11111111 11111111+0000000 00000000 00000000 00000001
= 1 00000000 00000000 00000000 00000000，操作完后再转回char 00000000 
2.正数的整型提升：char ch = 1;———— 00000001 <===> 00000000 00000000 00000000 00000001+0000000 00000000 00000000 00000001 <===> 0000 0010
*/
//int main()
//{
// char c = 1;	
// printf("%u\n", sizeof(c));			//char本身的字节
// printf("%u\n", sizeof(+c));		//+c操作之前“整型提升”，变为4个字节
// printf("%u\n", sizeof(!c));		//同理，操作之前被“整型提升”
// return 0; 
//}

//sizeof()中的表达式不参与计算,sizeof的返回值类型实际为无符号整形
//int a =10;
//short s =0;
//sizef(s = a+2)		//整型提升

/*********************************************************************************************************************************************************************/
//算术转换
//寻常算术转换：
//long double
//double
//float
//unsigned long int
//long int
//unsigned int
//int	
//从大到小的顺序
//当操作的表达式大于整型的表达式时，会进行算术转换，先令整型等于大的类型（向上转换）
//警告： 但是算术转换要合理，要不然会有一些潜在的问题。
//当浮点型转为整型，会丢失小数点，丢失精度

//例如
/*
#include <stdio.h>
int i;
int main()
{
    i--;
	// 算术转换，向上转换
    if (i > sizeof(i))
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    return 0; 
}
*/
//输出结果是 <  
//因为sizeof的返回值类型为无符号整型，表达式(整型 > 无符号整型) 因此编译器会自动将左侧i自动转换为无符号整形的数据，-1对应的无符号整形是一个非常大的数字
/*********************************************************************************************************************************************************************/




//************************************************************************************
//int math(int n,int m)
//{
//    int i;
//	int temp1 = 0;
//	int temp2 = 0;
//	for(i=0; i<32; i++)
//	{
//		if( ((n>>i)&1) == 1 )
//			temp1++; 
//	}
//    temp2 = 0;
//	 for(i=0; i<32; i++)
//	{
//		if( ((m>>i)&1) == 1 )
//			temp2++; 
//	}
//	temp2 = temp1^temp2;
//    return temp2;
//}
//上述错误在于 求的是不同位，而不是相差多少个1，有可能同位上都是1